/**
 * 
 */
package pacMan;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.util.ArrayList;

/**
 * 
 * Class: Wall
 * Draws a line on the screen that represents a barrier to movement for Actors on-screen. A Wall
 * is defined by multiple Coordinate objects, and a reference to the single Wall object is stored
 * in every 'tile' the line passes through in the ObjectLocationTracker grid.
 *
 * @author Tomas Rivera
 * @version $Id$
 */
public class Wall extends BasicDrawableObject {

	//////////////////////////////////////////////////////////
	// Constructor methods
	//////////////////////////////////////////////////////////
	
	/**
	 * Creates a Wall object.
	 * @param c
	 * @param w
	 */
	public Wall(Coordinate c, WindowParameters w) {
		super(c, w);
		wallPoints = new ArrayList<Coordinate>();
		this.markObstacle(true);
	}
	
	//////////////////////////////////////////////////////////
	// Public methods
	//////////////////////////////////////////////////////////
	
	/**
	 * Reconfigures the Wall object to represent two parallel lines with the ends of
	 * each line connected to one another.
	 * 
	 */
	public void configureDoubleLine() {
		
		// Add additional points to the Wall object so that we get doubled line
		// that sits within the tile.
		int size = wallPoints.size();
		for(int i = size - 1; i >= 0; i--) {
			Coordinate current = wallPoints.get(i);
			Coordinate next = null;
			if(i == (size - 1)) {
				next = wallPoints.get(i - 1);
				
				if(current.getX() != next.getX() &&
				   current.getY() == next.getY())
				{
					if(current.getX() < next.getX()) {
						// Move last point left 1/2 tile
						current.changePosition(current.getX() - (this.getWindowParams().getPixelScale() / 2), current.getY());
						
						// Set new point 1/2 tile below current one.
						wallPoints.add(new Coordinate(current.getX(), current.getY() + (this.getWindowParams().getPixelScale() / 2), this.getWindowParams()));
					} else {
						// Move last point right 1/2 tile
						current.changePosition(current.getX() + (this.getWindowParams().getPixelScale() / 2), current.getY());
						
						// Set new point 1/2 tile above current one.
						wallPoints.add(new Coordinate(current.getX(), current.getY() - (this.getWindowParams().getPixelScale() / 2), this.getWindowParams()));
					}
				} else if(current.getX() == next.getX() &&
						  current.getY() != next.getY()) {
					if(current.getY() < next.getY()) {
						// Move last point up 1/2 tile
						current.changePosition(current.getX(), current.getY() - (this.getWindowParams().getPixelScale() / 2));
						
						// Set new point 1/2 tile below current one.
						wallPoints.add(new Coordinate(current.getX() - (this.getWindowParams().getPixelScale() / 2), current.getY(), this.getWindowParams()));
					} else {
						// Move last point down 1/2 tile
						current.changePosition(current.getX(), current.getY() + (this.getWindowParams().getPixelScale() / 2));
						
						// Set new point 1/2 tile above current one.
						wallPoints.add(new Coordinate(current.getX() + (this.getWindowParams().getPixelScale() / 2), current.getY(), this.getWindowParams()));
					}
				} else {
					assert false;
				}
			} else {
				Coordinate last = wallPoints.get(i + 1);
				if(i == 0) {
					next = this.getObjectLocation();
				} else {
					next = wallPoints.get(i - 1);
				}
				
				if(current.getX() != last.getX() &&
				   current.getY() == last.getY())
				{
					if(last.getX() < current.getX()) {
						// Heading East
						if(current.getY() < next.getY()) {
							// Turning South
							wallPoints.add(new Coordinate(current.getX() - (this.getWindowParams().getPixelScale() / 2), current.getY() + (this.getWindowParams().getPixelScale() / 2), this.getWindowParams()));
						} else {
							// Turning North
							wallPoints.add(new Coordinate(current.getX() + (this.getWindowParams().getPixelScale() / 2), current.getY() + (this.getWindowParams().getPixelScale() / 2), this.getWindowParams()));
						}
					} else {
						// Heading West
						if(current.getY() < next.getY()) {
							// Turning South
							wallPoints.add(new Coordinate(current.getX() - (this.getWindowParams().getPixelScale() / 2), current.getY() - (this.getWindowParams().getPixelScale() / 2), this.getWindowParams()));
						} else {
							// Turning North
							wallPoints.add(new Coordinate(current.getX() + (this.getWindowParams().getPixelScale() / 2), current.getY() - (this.getWindowParams().getPixelScale() / 2), this.getWindowParams()));
						}
					}
				} else if(current.getX() == last.getX() &&
						  current.getY() != last.getY()) {
					if(last.getY() < current.getY()) {
						// Heading South
						if(current.getX() < next.getX()) {
							// Turning West
							wallPoints.add(new Coordinate(current.getX() - (this.getWindowParams().getPixelScale() / 2), current.getY() + (this.getWindowParams().getPixelScale() / 2), this.getWindowParams()));
						} else {
							// Turning East
							wallPoints.add(new Coordinate(current.getX() - (this.getWindowParams().getPixelScale() / 2), current.getY() - (this.getWindowParams().getPixelScale() / 2), this.getWindowParams()));
						}
					} else {
						// Heading North
						if(current.getX() < next.getX()) {
							// Turning East
							wallPoints.add(new Coordinate(current.getX() + (this.getWindowParams().getPixelScale() / 2), current.getY() + (this.getWindowParams().getPixelScale() / 2), this.getWindowParams()));
						} else {
							// Turning West
							wallPoints.add(new Coordinate(current.getX() + (this.getWindowParams().getPixelScale() / 2), current.getY() - (this.getWindowParams().getPixelScale() / 2), this.getWindowParams()));
						}
					}
				} else {
					assert false;
				}

				if(i == 0) {
					last = wallPoints.get(0);
					current = this.getObjectLocation();
					
					if(current.getX() != last.getX() &&
					   current.getY() == last.getY())
					{
						if(last.getX() < current.getX()) {
							// Heading East
							// Move first point east 1/2 tile
							this.getObjectLocation().changePosition(this.getObjectLocation().getX() + (this.getWindowParams().getPixelScale() / 2), this.getObjectLocation().getY());
							
							// Set new point 1/2 tile below current one.
							wallPoints.add(new Coordinate(current.getX(), current.getY() + (this.getWindowParams().getPixelScale() / 2), this.getWindowParams()));
						} else {
							// Heading West
							// Move first point west 1/2 tile
							this.getObjectLocation().changePosition(this.getObjectLocation().getX() - (this.getWindowParams().getPixelScale() / 2), this.getObjectLocation().getY());
							
							// Set new point 1/2 tile above current one.
							wallPoints.add(new Coordinate(current.getX(), current.getY() - (this.getWindowParams().getPixelScale() / 2), this.getWindowParams()));
						}
					
					} else if(current.getX() == last.getX() &&
							  current.getY() != last.getY()) 
					{
						if(last.getY() < current.getY()) {
							// Heading South
							// Move first point south 1/2 tile
							this.getObjectLocation().changePosition(this.getObjectLocation().getX(), this.getObjectLocation().getY() + (this.getWindowParams().getPixelScale() / 2));
							
							// Set new point 1/2 tile left of current one.
							wallPoints.add(new Coordinate(current.getX() - (this.getWindowParams().getPixelScale() / 2), current.getY(), this.getWindowParams()));
							
						} else {
							// Heading North
							// Move first point north 1/2 tile
							this.getObjectLocation().changePosition(this.getObjectLocation().getX(), this.getObjectLocation().getY() - (this.getWindowParams().getPixelScale() / 2));
							
							// Set new point 1/2 tile below current one.
							wallPoints.add(new Coordinate(current.getX() + (this.getWindowParams().getPixelScale() / 2), current.getY(), this.getWindowParams()));
						}
						
					} else {
						assert false;
					}
					
					// Close off the double line.
					wallPoints.add(this.getObjectLocation());
				}
			}
		}
	}
	
	/**
	 * Adds an additional point to the line that represents the Wall. This
	 * Coordinate should be centered within a tile.
	 * 
	 * @param c
	 */
	public void addCoordinate(Coordinate c) {
		wallPoints.add(c);
	}
	
	/**
	 * Get the list of Coordinates that represent this Wall on-screen.
	 * 
	 * @return
	 */
	public ArrayList<Coordinate> getCoordinates() { return wallPoints; }

	//////////////////////////////////////////////////////////
	// Overridden methods
	//////////////////////////////////////////////////////////
	
	/*
	 * (non-Javadoc)
	 * @see pacMan.BasicDrawableObject#drawObject(java.awt.Graphics2D)
	 */
	@Override
	public void drawObject(Graphics2D d) {
		Coordinate startPoint = getObjectLocation();
		
		d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
        d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
        d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
        d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
        d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
		
		if(wallPoints.size() == 1) {
			d.setColor(Color.WHITE);
			d.setStroke(new BasicStroke(5));
			d.drawLine(startPoint.getX() - (getWindowParams().getPixelScale() / 2), 
					startPoint.getY() + (getWindowParams().getPixelScale() / 4), 
					wallPoints.get(0).getX() + (getWindowParams().getPixelScale() / 2), 
					wallPoints.get(0).getY() + (getWindowParams().getPixelScale() / 4));
		} else {
			d.setColor(getWindowParams().getWallColor());
			d.setStroke(new BasicStroke(3));
			for(Coordinate endPoint : wallPoints) {
				d.drawLine(startPoint.getX(), startPoint.getY(), endPoint.getX(), endPoint.getY());
				startPoint = endPoint;
			}
		}

	}
	
	/*
	 * (non-Javadoc)
	 * @see pacMan.BasicDrawableObject#resizeObject()
	 */
	@Override
	public void resizeObject() {
		getObjectLocation().update(getWindowParams().getWindowWidth(), getWindowParams().getWindowHeight());
		for(Coordinate c : wallPoints) {
			if(c == getObjectLocation()) continue;
			c.update(getWindowParams().getWindowWidth(), getWindowParams().getWindowHeight());
		}
	}
	
	/*
	 * (non-Javadoc)
	 * @see pacMan.BasicDrawableObject#getGridLocationX()
	 */
	@Override
	public int getGridLocationX() {
		System.err.println("Attempted to query location for an object that exists in multiple grid locations.");
		System.exit(1);
		return -1;
	}
	
	/*
	 * (non-Javadoc)
	 * @see pacMan.BasicDrawableObject#getGridLocationY()
	 */
	@Override
	public int getGridLocationY() {
		System.err.println("Attempted to query location for an object that exists in multiple grid locations.");
		System.exit(1);
		return -1;
	}
	
	//////////////////////////////////////////////////////////
	// Private member variables
	//////////////////////////////////////////////////////////
	
	ArrayList<Coordinate> wallPoints;
}
